package com.example.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.common.ActionEnum;
import com.example.common.Result;
import com.example.common.ResultUtil;
import com.example.common.VerifyUtil;
import com.example.common.exception.BusinessRuntimeException;
import com.example.entity.ExecuteTaskRequest;
import com.example.entity.TaskRequestQuery;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.util.CollectionUtil;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricActivityInstanceQuery;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 流程任务
 */
@Transactional
@Slf4j
@RestController
@RequestMapping("/runtime/tasks")
public class TaskController {

    // 流程引擎
    @Qualifier("processEngine")
    @Autowired
    private ProcessEngine processEngine;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;

    //根据用户ID或者用户组ID，查询该用户代办，查询该用户代办数量
    @GetMapping("/count")
    public Result count(String userId, List<String> groupIds) {
        long count = 0;
        if (CollectionUtil.isEmpty(groupIds)) {
            count = taskService.createTaskQuery().taskCandidateOrAssigned(userId).orderByTaskPriority().desc()
                    .orderByTaskCreateTime().asc()
                    .count();
        } else {
            count = taskService.createTaskQuery().taskCandidateOrAssigned(userId).taskCandidateGroupIn(groupIds).orderByTaskPriority().desc()
                    .orderByTaskCreateTime().asc()
                    .count();
        }
        return new ResultUtil<>().setData(count);
    }

    //根据用户ID或者用户组ID，查询该用户待办
    /*   参数
     *   businessKey: ""
     *   businessName: ""
     *   businessType: ""
     *   current: 1
     *   groupIds: ["ROLE_ADMIN", "ROLE_GROUP_LEADER"]
     *   size: 20
     *   userId: "admin"
     */
    @PostMapping
    public Result page(@RequestBody TaskRequestQuery query) {
        int firstResult = (int) ((query.getCurrent() - 1) * query.getSize());
        int maxResults = (int) (query.getCurrent() * query.getSize());
        TaskQuery taskQuery = taskService.createTaskQuery();// 查询条件
        if (StringUtils.isNotBlank(query.getUserId())) {  //代办人ID
            taskQuery.taskCandidateOrAssigned(query.getUserId());
        }
        if (query.getGroupIds() != null && query.getGroupIds().size() > 0) { //代办分组ID,例：["ROLE_ADMIN", "ROLE_GROUP_LEADER"]
            taskQuery.taskCandidateGroupIn(query.getGroupIds());
        }
        if (StringUtils.isNotBlank(query.getBusinessKey())) {
            taskQuery.processInstanceBusinessKey(query.getBusinessKey());
        }
//        if (StringUtils.isNotBlank(query.getBusinessType())) {
//            taskQuery.processVariableValueEquals("businessType", query.getBusinessType());
//        }
//        if (StringUtils.isNotBlank(query.getBusinessName())) {
//            taskQuery.processVariableValueLike("businessName", query.getBusinessName());
//        }
        if (StringUtils.isNotBlank(query.getProcessInstanceId())) {
            taskQuery.processInstanceId(query.getProcessInstanceId());
        }
        /** 查询
         * */
        List<Task> taskList = taskQuery
                .includeProcessVariables()//在任务查询结果中包含全局任务变量
                .orderByTaskPriority().desc() //通过 TaskPriority 排序
                .orderByTaskCreateTime().desc() //通过 TaskCreateTime 排序
                .listPage(firstResult, maxResults);
        JSONArray arr = new JSONArray();
        taskList.forEach(task -> {
            JSONObject obj = new JSONObject();
            obj.put("id", task.getId());
            obj.put("assignee", task.getAssignee());
            obj.put("name", task.getName());
            obj.put("variables", task.getProcessVariables());
            obj.put("createTime", task.getCreateTime());
            obj.put("processInstanceId", task.getProcessInstanceId());//查询流程图
            obj.put("executionId", task.getExecutionId());
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            String businessKey = processInstance.getBusinessKey();
            String businessName = processInstance.getName();
            String processDefinitionId = processInstance.getProcessDefinitionId();
            String processDefinitionKey = processInstance.getProcessDefinitionKey();
            String processDefinitionName = processInstance.getProcessDefinitionName();
            obj.put("businessKey", businessKey);
            obj.put("businessName", businessName);
            obj.put("processDefinitionId", processDefinitionId);
            obj.put("processDefinitionKey", processDefinitionKey);
            obj.put("processDefinitionName", processDefinitionName);
            arr.add(obj);
        });
        Long count = taskQuery.count();
        Page page = new Page(query.getCurrent(), query.getSize(), count);
        page.setRecords(arr);
        return new ResultUtil<>().setData(page);
    }




    //查询已办任务

    /**
     * userId 用户ID
     * current 页码
     * size 数量
     *
     * businessName
     * businessType
     * businessKey
     * */

    // *********************************************    操作表：    act_hi_taskinst
    @GetMapping(value = "/history")
    public Result hisPage(@RequestParam Map map) {
        String userId = map.get("userId") + "";
        int size = 10;
        int current = 1;
        int firstResult = (current - 1) * size;// 第一条数据
        int maxResults = size;// 每页多少条

        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery();

        if (StringUtils.isNotBlank(userId)) {
            query.taskAssignee(userId);
        }

        if (map.get("businessKey") != null) {
            query.processInstanceBusinessKey(map.get("businessKey") + "");
        }
        if (map.get("businessName") != null) {
            query.processVariableValueEquals("businessName", map.get("businessName") + "");
        }
        if (map.get("businessType") != null) {
            query.processVariableValueLike("businessType", map.get("businessType") + "");
        }

        List<HistoricTaskInstance> historicTaskInstances = query.finished()
                .includeProcessVariables().orderByHistoricTaskInstanceEndTime()
                .desc()
                .listPage(firstResult, maxResults);


        JSONArray arr = new JSONArray();
        historicTaskInstances.forEach(historicTaskInstance -> {
            JSONObject obj = new JSONObject();
            obj.put("id", historicTaskInstance.getId());
            obj.put("executionId", historicTaskInstance.getExecutionId());
            obj.put("processInstanceId", historicTaskInstance.getProcessInstanceId());
            obj.put("processDefinitionId", historicTaskInstance.getProcessDefinitionId());
            obj.put("taskDefinitionId", historicTaskInstance.getTaskDefinitionId());
            obj.put("assignee", historicTaskInstance.getAssignee());//签收人或被委托
            obj.put("createTime", historicTaskInstance.getCreateTime());
            obj.put("endTime", historicTaskInstance.getEndTime());
            obj.put("durationInMillis", historicTaskInstance.getDurationInMillis()); //审核过程耗时
            obj.put("name", historicTaskInstance.getName());
            obj.put("owner", historicTaskInstance.getOwner());//实际签收人 任务的拥有者
            obj.put("variables", historicTaskInstance.getProcessVariables());
            //查询业务主键
            HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(historicTaskInstance.getProcessInstanceId()).singleResult();
            obj.put("businessKey", processInstance.getBusinessKey());
            obj.put("businessName", processInstance.getName());
            obj.put("processDefinitionKey", processInstance.getProcessDefinitionKey());
            obj.put("processDefinitionName", processInstance.getProcessDefinitionName());
            arr.add(obj);
        });
        long count = query.count();
        Page page = new Page(current, size, count);
        page.setRecords(arr);
        return new ResultUtil<>().setData(page);
    }

    //执行任务
    /*  /taskId
        {
            "action":"complete",
            "localScope":false,
            "assignee":"1",
            "variables":{}
        }
    * */
    @PostMapping(value = "/{taskId}")
    //任务执行类型 claim：签收 unclaim 反签收 complete 完成 delegate 任务委派 resolve 任务签收完成 返回任务人 assignee 任务转办
    public Result executeTask(@PathVariable String taskId, @RequestBody ExecuteTaskRequest executeTaskRequest) {


        // ********** 多实例 **********
        Map<String,Object> map = new HashMap();
        List<String> list = new ArrayList<>();
        list.add("10");
        list.add("11");
        list.add("12");
        map.put("groupList", list);
        executeTaskRequest.setVariables(map);
        // ********** 多实例 **********


        String action = executeTaskRequest.getAction();
        String userId = executeTaskRequest.getAssignee();
        Boolean localScope = executeTaskRequest.getLocalScope();
        Map<String, Object> variables = executeTaskRequest.getVariables();
        log.info("-----签收任务ID:{},签收类型:{},签收人ID:{},---------", taskId, action, userId);
        localScope = (null == localScope) ? false : localScope;
        Map<String, Object> result = Maps.newHashMap();
        ActionEnum actionEnum = ActionEnum.actionOf(action);
        switch (actionEnum) {
            case COMPLETE:
                //完成任务
                result = this.complete(taskId, variables, localScope);
                break;
            case CLAIM:
                //签收任务
                this.claim(taskId, userId);
                break;
            case UNCLAIM:
                //反签收
                this.unClaim(taskId);
                break;
            case DELEGATE:
                //任务委派
                this.delegate(taskId, userId);
                break;
            case RESOLVE:
                //委派任务完成，归还委派人
                this.resolveTask(taskId);
                break;
            case ASSIGNEE:
                //任务转办
                this.setAssignee(taskId, userId);
                break;
            default:
                break;
        }
        return new ResultUtil<>().setData(result);
    }

    public void claim(String taskId, String userId) {
        log.info("-----签收任务ID:{}，签收人ID:{}---------", taskId, userId);
        taskService.claim(taskId, userId);
    }

    public void unClaim(String taskId) {
        log.info("-----反签收任务ID:{}---------", taskId);
        taskService.unclaim(taskId);
    }

    public void complete(String taskId) {
        this.complete(taskId, null);
        log.info("-----------任务ID：{},已完成-----------", taskId);
    }

    public void complete(String taskId, Map<String, Object> variables) {
        log.info("-----完成任务ID:{}---------", taskId);
        taskService.complete(taskId, variables);
    }

    public Map<String, Object> complete(String taskId, Map<String, Object> variables, boolean localScope) {

//      签收
//        taskService.claim(taskId, "user002");
        //修改执行人 其实我这里就相当于签收了
//        taskService.setAssignee(taskId, "user001");

        Task finishTask = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (finishTask == null) {
            throw new RuntimeException("通过taskId：" + taskId + "为查询到task");
        }
        /**complete()
         * 当任务成功执行时调用，并由最终用户提供所需的任务参数。
         *
         * @param taskId  要完成的任务的id不能为null。
         * @param variables 任务参数。可以为空。
         * @param localScope 如果为真，所提供的变量将存储在任务本地，而不是流程实例范围(这是{@link #complete(String, Map)}的默认值)。
         * @throws FlowableObjectNotFoundException 当给定id没有任务存在时。
         */
        try {
            taskService.complete(taskId, variables, localScope);
        } catch (FlowableObjectNotFoundException e) {
            // taskId 不存在
        }
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(finishTask.getProcessInstanceId()).active().list();
        Map<String, Object> map = new HashMap<>(16);
        Map<String, Object> finish = new HashMap<>(16);
        finish.put("id", finishTask.getId());
        finish.put("name", finishTask.getName());
        map.put("finish", finish);
        List<Map<String, Object>> taskList = new ArrayList<>();
        for (Task task : tasks) {
            Map<String, Object> taskMap = new HashMap<>(16);
            taskMap.put("id", task.getId());
            taskMap.put("name", task.getName());
            taskMap.put("processInstanceId", task.getProcessInstanceId());
            taskMap.put("executionId", task.getExecutionId());
            taskList.add(taskMap);
        }
        map.put("active", taskList);
        return map;
    }

    public void setAssignee(String taskId, String userId) {
        log.info("-----移交任务ID:{},移交给用户ID:{}---------", taskId, userId);
        taskService.setAssignee(taskId, userId);
    }

    public void delegate(String taskId, String userId) {
        log.info("-----委派任务ID:{},委派给用户ID:{}---------", taskId, userId);
        taskService.delegateTask(taskId, userId);
    }

    public void resolveTask(String taskId) {
        log.info("-----委派完成任务ID:{}---------", taskId);
        taskService.resolveTask(taskId);
    }

    public void delete(String taskId) {
        log.info("-----删除任务：任务ID:{}---------", taskId);
        taskService.deleteTask(taskId);
    }



    //查询任务
    @GetMapping
    public Result getTask(TaskRequestQuery taskRequestQuery) {
        TaskQuery taskQuery = taskService.createTaskQuery();
        if(StringUtils.isNotBlank(taskRequestQuery.getTaskId())){
            //任务id查询单条记录
            Task task = taskQuery.taskId(taskRequestQuery.getTaskId()).includeProcessVariables().singleResult();
//            TaskVO taskVO = new TaskVO();
//            BeanUtil.copyProperties(task,taskVO,"variables");
//            taskVO.setVariables(task.getProcessVariables());
//            return Lists.newArrayList(taskVO);
            return new ResultUtil<>().setData(task);
        }
        if(StringUtils.isNotBlank(taskRequestQuery.getProcessInstanceId())){
            taskQuery.processInstanceId(taskRequestQuery.getProcessInstanceId());
        }
        List<Task> tasks = taskQuery.includeProcessVariables().list();
        return new ResultUtil<>().setData(tasks);
    }


    /* 添加批注信息
     *  taskId: 任务ID
     *  processInstanceId:流程实例ID
     *  message: 批注信息
     *  userId
     * */
    @PostMapping(value = "/comment")
    public Result addComments(@RequestBody Map map) {
        String taskId = map.get("taskId") + "";
        String processInstanceId = map.get("processInstanceId") + "";
        String message = map.get("message") + "";
        String userId = map.get("userId") + "";
        log.info("----任务或者流程实例添加备注：任务ID:{},流程实例ID{}---------", taskId, processInstanceId);
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new RuntimeException("通过：" + taskId + "未查询到Task");
        }
        Comment comment = taskService.addComment(taskId, processInstanceId, message);
        comment.setUserId(userId);
        taskService.saveComment(comment);
        return new ResultUtil<>().setSuccessMsg("添加批注信息成功");
    }

    /*查询批注信息
     * processInstanceId： 流程实例ID
     * */
    @GetMapping(value = "/comment")
    public Result getTaskComments(String processInstanceId) {
        List<Comment> comments = taskService.getProcessInstanceComments(processInstanceId);
        return new ResultUtil<>().setData(comments);
    }

    /**
     * 功能描述: 任务撤回【注意：当前与目标定义Key为设计模板时任务对应的ID(通常格式：sid-xxxxxxxxxxxxxxxxx),而非数据主键ID】
     * @author: 庞留杰
     * @param processInstanceId ：流程实例ID
     * @param currentTaskKey ：当前任务定义Key
     * @param targetTaskKey ：目标任务定义Key
     * @return:
     * @exception
     * @date: 2021/1/21 11:18
     */
    @PutMapping
    public Result withdraw(String processInstanceId, String currentTaskKey, String targetTaskKey) {
        log.info("----任务撤回：流程实例ID:{},当前活动任务ID:{},撤回到达的任务ID:{} ---------", processInstanceId, currentTaskKey, targetTaskKey);
        VerifyUtil.notEmpty("processInstanceId", processInstanceId);
        VerifyUtil.notEmpty("currentTaskKey", currentTaskKey);
        VerifyUtil.notEmpty("targetTaskKey", targetTaskKey);
        try {
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            if(pi == null){
                throw new BusinessRuntimeException("通过流程实例ID[" + processInstanceId + "]，未查到流程，请确认流程实例ID是否正确");
            }
            Process process = repositoryService.getBpmnModel(pi.getProcessDefinitionId()).getMainProcess();
            process.getFlowElements().forEach(it->{
                System.out.println(it.getName()+" / "+it.getId());
            });

            /* ParallelGateway并行网关
             *  InclusiveGateway 包容网关
             *  MultiInstanceActivityBehavior 多实例
             */
            // 通过flowElementId查询所有节点，包含子节点，如果searchRecursive=false，或者不传，不会查询子流程内的节点
            Activity targetFlowElement = (Activity) process.getFlowElement(targetTaskKey, true);
            if(targetFlowElement == null){
               throw new BusinessRuntimeException("通过目标节点ID["+targetTaskKey+"]未查到节点信息，请确认目标节点ID是否正确");
            }
            String targetName = targetFlowElement.getName();
            List<SequenceFlow> incomingFlows = targetFlowElement.getIncomingFlows();// 获取的值 (上个节点sid->当前节点的sid)
            if (CollectionUtils.isNotEmpty(incomingFlows)) {
                for (SequenceFlow sequenceFlow : incomingFlows) {
                    FlowElement upNode = sequenceFlow.getSourceFlowElement();
                    /***********************************并行网关*********************************/
                    if (upNode instanceof ParallelGateway) {// 并行网关
                        String msg = String.format("回退的节点“%s”处于并行网关中，不允许回退，请选择其他节点回退", targetName);
                        return new ResultUtil<>().setErrorMsg(msg);
                    }
                    /***********************************包容网关*********************************/
                    if (upNode instanceof InclusiveGateway) {// 包容网关
                        String msg = String.format("回退的节点“%s”处于包容网关中，不允许回退，请选择其他节点回退", targetName);
                        return new ResultUtil<>().setErrorMsg(msg);
                    }
                }
            }
            /***********************************多实例*********************************/
            if (isSubprocessActive(process, targetTaskKey)) {//当前节点在子流程内部
                if (targetFlowElement instanceof UserTask) {//只是普通节点
                    FlowElementsContainer parentContainer = targetFlowElement.getParentContainer();
                    Activity activity = (Activity) parentContainer;
                    if (activity.getBehavior() instanceof MultiInstanceActivityBehavior) {//判断父节点是否是多实例
                        String msg = String.format("回退的节点“%s”，处于多实例的子流程中，不允许回退，请选择其他节点回退", targetName);
                        return new ResultUtil<>().setErrorMsg(msg);
                    }
                } else {
                    Activity activity = (Activity) targetFlowElement;
                    if (activity != null && activity.getBehavior() instanceof MultiInstanceActivityBehavior) {//多实例
                        String msg = String.format("回退的节点“%s”属于多实例节点，不允许回退，请选择其他节点回退", targetName);
                        return new ResultUtil<>().setErrorMsg(msg);
                    }
                }
            } else {
                if (!(targetFlowElement instanceof UserTask)) {//不是普通节点
                    Activity activity = (Activity) targetFlowElement;
                    if (activity != null && activity.getBehavior() instanceof MultiInstanceActivityBehavior) {//多实例
                        String msg = String.format("回退的节点“%s”属于多实例节点，不允许回退，请选择其他节点回退", targetName);
                        return new ResultUtil<>().setErrorMsg(msg);
                    }
                }
            }

//            //判断该节点上一个节点是不是并行网关节点
//            Activity distActivity = (Activity) process.getFlowElement(targetTaskKey, true);
//            List<SequenceFlow> incomingFlows = distActivity.getIncomingFlows();// 获取的值 (上个节点sid->当前节点的sid)
//            if(CollectionUtils.isNotEmpty(incomingFlows)){
//                FlowElement targetFlowElement = process.getFlowElement(targetTaskKey, true);//目标节点，可能在子流程中
//                String targetName  = targetFlowElement.getName();
//                for (SequenceFlow sequenceFlow : incomingFlows) {
//                    FlowElement upNode = sequenceFlow.getSourceFlowElement();
//                     /* ParallelGateway并行网关
//                     *  InclusiveGateway 包容网关
//                     *  MultiInstanceActivityBehavior 多实例
//                     */
//                    if (upNode instanceof ParallelGateway) {// 并行网关
//                        String msg = String.format("回退的节点“%s”处于并行网关中，不允许回退，请选择其他节点回退", targetName);
//                        return new ResultUtil<>().setErrorMsg(msg);
//                    }
//                    if (upNode instanceof InclusiveGateway) {// 包容网关
//                        String msg = String.format("回退的节点“%s”处于包容网关中，不允许回退，请选择其他节点回退", targetName);
//                        return new ResultUtil<>().setErrorMsg(msg);
//                    }
//                    Activity multiActivity = (Activity) upNode;
//                    if (multiActivity != null && multiActivity.getBehavior() instanceof MultiInstanceActivityBehavior) {//多实例
//                        String msg = String.format("回退的节点“%s”处于多实例中，不允许回退，请选择其他节点回退", targetName);
//                        return new ResultUtil<>().setErrorMsg(msg);
//                    }
//                }
//            }
            // 流程回退到上一个节点，审批人继续审批
            runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                    .moveActivityIdTo(currentTaskKey, targetTaskKey).changeState();
        } catch (FlowableObjectNotFoundException e) {
            log.info("报错：{}", e);
            throw new BusinessRuntimeException("任务撤回失败");
        }
        return new ResultUtil<>().setSuccessMsg("任务撤回成功");
    }

    // 判断是否是子流程中的节点:false:不是，true:是
    public boolean isSubprocessActive(Process process ,String activityId){
        FlowElement flowElement1 = process.getFlowElement(activityId, false);
        FlowElement flowElement2 = process.getFlowElement(activityId, true);
        if(flowElement1 == null && flowElement2 == null){
            throw new BusinessRuntimeException(String.format("通过activityId:%s，查询不到该节点",activityId));
        }
        if(flowElement1 != null){
            return false;
        } else {
            return true;
        }
    }

    /**
     * 功能描述: 回退，注意只能回退一步【例： 当前在“第三步”：
     *                              调用一次，回退到“第二步”，
     *                              再次调用，回退到“第三步”，
     *                              再次调用，回退到“第二步”，
     *                                以此类推。。。。
     *           】
     * @author: 庞留杰
     * @param processInstanceId：流程实例ID
     * @return:
     * @exception
     * @date: 2021/1/21 11:20
     */
    @PutMapping("rollback")
    public Result rollback(String processInstanceId) {
        VerifyUtil.notEmpty("processInstanceId", processInstanceId);
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if(task == null){
            throw new BusinessRuntimeException("流程实例processInstanceId：" + processInstanceId + "，不正确");
        }
        String currentTaskKey = task.getTaskDefinitionKey();//当前节点id
        //过滤历史节点类型 只要:任务节点类型的
        List<String> activityTypeFilter = Lists.newArrayList("userTask");
        //审批历史数据
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceEndTime().desc().list().stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());
        if (collect.size() <= 0) {
            throw new BusinessRuntimeException("流程尚未未开始审核，无法回退");
        }
        String targetTaskKey = collect.get(0).getActivityId();//审批历史中上一个节点id
        collect.forEach(it->{
            log.info(">>>>>>>>>>>>>历史节点>>>>>>>>>>>>>"+it);
        });
        log.info("========= 任务回退：流程实例ID:{}，当前审批节点-ID:{}-NAME:{}，回退节点ID:{}-NAME:{} =========",
                processInstanceId,
                task.getTaskDefinitionKey(), task.getName(),
                collect.get(0).getActivityId(), collect.get(0).getActivityName());





        try {
            // 流程回退到上一个节点，审批人继续审批
            runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                    .moveActivityIdTo(currentTaskKey, targetTaskKey).changeState();
        } catch (FlowableObjectNotFoundException e) {
            log.info("报错：{}", e);
            throw new BusinessRuntimeException("任务回退失败");
        }
        return new ResultUtil<>().setSuccessMsg("任务回退成功");
    }


    /* 查询流程记录, 进入办理页面查看任务执行的详情
     *  processInstanceId:流程实例ID
     * */
    @GetMapping(value = "/records")
    public Result records(String processInstanceId,String executionId) {
        List<String> filterEvents = null;
        //过滤历史节点类型 只要开始 结束 任务节点类型的
        if (CollectionUtil.isEmpty(filterEvents)){
            //,"sequenceFlow" ,"serviceTask"
            filterEvents = Lists.newArrayList("startEvent", "endEvent", "userTask");
        }
        List<String> activityTypeFilter = filterEvents;
        // List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
        HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
        historicActivityInstanceQuery.processInstanceId(processInstanceId);
        // 多实例 todo 这个有问题，只能显示多实例内部的，外部的流程节点都会过滤掉
        //  if(StringUtils.isNotEmpty(executionId)){
        //     historicActivityInstanceQuery.executionId(executionId);
        //  }
        List<HistoricActivityInstance> collect = historicActivityInstanceQuery.finished()//查询已经结束的记录
                .orderByHistoricActivityInstanceEndTime().desc().list()//排序字段，方式
                .stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());//过滤需要的类型
        // 增加备注信息
        List list = new ArrayList();
        for (HistoricActivityInstance historic : collect) {
            Map map = new HashMap();
            List<Comment> taskComments = taskService.getTaskComments(historic.getTaskId());
            map.put("historic",historic);
            map.put("comments",taskComments);
            list.add(map);
        }
        return new ResultUtil<>().setData(list);
    }



    //测试。。。。。。。。。。。
    @GetMapping("/claimTest")
    public Result claimTest(String taskId, String userId) {
        log.info("-----签收任务ID:{}，签收人ID:{}---------", taskId, userId);
        taskService.claim(taskId, userId);
        return new ResultUtil<>().setSuccessMsg("签收成功");
    }




    @GetMapping("test")
    public void test(String processInstanceId) {
        List<String> filterEvents = null;
        //过滤历史节点类型 只要开始 结束 任务节点类型的
        if (CollectionUtil.isEmpty(filterEvents)){
            filterEvents = Lists.newArrayList("userTask");
        }
        List<String> activityTypeFilter = filterEvents;
        //获取HistoryService实例
        HistoryService historyService= processEngine.getHistoryService();
        //添加查询条件
        List<HistoricActivityInstance> activities =
                historyService.createHistoricActivityInstanceQuery()
                        //选择特定实例
                        .processInstanceId(processInstanceId)
                        //选择已完成的
                        .finished()
                        //根据实例完成时间升序排列
//                        .orderByHistoricActivityInstanceEndTime().asc()
                        .list()
                        .stream().filter(his -> activityTypeFilter.contains(his.getActivityType()))
                        .sorted(Comparator.comparing(HistoricActivityInstance::getStartTime))//排序
                        .collect(Collectors.toList());

        SimpleDateFormat time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        for (HistoricActivityInstance activity : activities) {
            System.out.println(activity.getActivityId() + " -- " +
                    activity.getActivityType() + " -- " +
                    activity.getActivityName() + " -- " +
                    (activity.getStartTime() != null ? time.format(activity.getStartTime()) : "") + " -- " +
                    (activity.getEndTime() != null ? time.format(activity.getEndTime()) : "")
            );
        }
    }

    @Autowired
    RepositoryService repositoryService;

    @GetMapping("test1")
    public void test1(String processInstanceId) {
        List<HistoricActivityInstance> arr = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).orderByHistoricActivityInstanceId().asc().list();
        String processDefinitionId = arr.get(0).getProcessDefinitionId();
        // 获取所有节点信息，暂不考虑子流程情况
        List<Process> processes =repositoryService.getBpmnModel(processDefinitionId).getProcesses();
//        List<List<NextNode>> nextNodes = new ArrayList<>();
        for (Process process : processes) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            if (CollectionUtils.isNotEmpty(flowElements)) {
                for (FlowElement flowElement : flowElements) {
                    if (flowElement instanceof UserTask) {
                        System.out.println("UserTask：" + flowElement.getName()+"==="+flowElement.getId()
                                +"----"+ ((UserTask) flowElement).getCandidateUsers()
                                +"----"+ ((UserTask) flowElement).getIncomingFlows()
                        );
                        //业务操作
                    }
                    if (flowElement instanceof SubProcess) {
                        //，，，
                    }
                }
            }
        }
    }


    /**
     * 获取所有可回退的节点
     * @param processInstanceId
     * @return
     */
    @GetMapping(value = "/getFallbackNode/{processInstanceId}")
    public Result getFallbackNode(@PathVariable(value = "processInstanceId") String processInstanceId) {
        //1.获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        String processDefinitionId = processInstance.getProcessDefinitionId();
        // 获取所有节点信息，暂不考虑子流程情况
        List<Process> processes =repositoryService.getBpmnModel(processDefinitionId).getProcesses();
        List<Map<String,String>> list = Lists.newArrayList();
        for (Process process : processes) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            if (CollectionUtils.isNotEmpty(flowElements)) {
                for (FlowElement flowElement : flowElements) {
                    // 用户task
                    if (flowElement instanceof UserTask) {
                        Map<String,String> map = Maps.newHashMap();
                        //业务操作
                        map.put("id",flowElement.getId());
                        map.put("name",flowElement.getName());
                        list.add(map);
                        System.out.println("一级节点===>"+flowElement.getName() + "  sid===>" + flowElement.getId());
                    }
                    // 子流程
                    if (flowElement instanceof SubProcess) {
                        for (FlowElement flowElement1 : ((SubProcess) flowElement).getFlowElements()) {
                            if(flowElement1 instanceof UserTask){
                                System.out.println("二级节点===>" + flowElement1.getName() + "  sid===>" + flowElement1.getId());
                            }
                            if (flowElement1 instanceof SubProcess) {
                                for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) {
                                    if(flowElement2 instanceof UserTask){
                                        System.out.println("三级节点===>" + flowElement2.getName() + "  sid===>" + flowElement2.getId());
                                    }
                                    if (flowElement2 instanceof SubProcess) {
                                        // todo

                                        System.out.println("==========="+((SubProcess) flowElement1).getFlowElements());


                                    }
                                }
                            }
                        }
                    }
                }
            }


        }
        list.sort((a, b) -> a.get("id").compareTo(b.get("id")));//排序
        // 获取当前的task
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
        list = list.stream().filter(it-> it.get("id").compareTo(task.getTaskDefinitionKey()) < 0 ).collect(Collectors.toList());//过滤
        Map<String,Object> map = Maps.newHashMap();
        map.put("history_nodes",list);
        map.put("current_node",task.getTaskDefinitionKey());
        map.put("current_name",task.getName());
        return new ResultUtil<>().setData(map);
    }


    /**
     * 功能描述: 获取所有可回退的节点
     *          规则：
     *          1.从历史获取所有审批过的节点信息
     *          2.通过开始时间排序，去除历史中重复的节点
     * @author: 庞留杰
     * @param   processInstanceId
     * @return:
     * @exception
     * @date: 2021/1/21 15:10
     */
    @GetMapping(value = "/v2/getFallbackNode/{processInstanceId}")
    public Result getFallbackNode2(@PathVariable(value = "processInstanceId") String processInstanceId) {
        VerifyUtil.notEmpty("processInstanceId",processInstanceId);

        // 当前task,不支持并行网关
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
        //过滤历史节点类型 只要开始 结束 任务节点类型的
        List<String> activityTypeFilter = Lists.newArrayList( "userTask");// 可以过滤的值："startEvent", "endEvent", "userTask" ,"sequenceFlow"等
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished()
                .orderByHistoricActivityInstanceStartTime().asc().list()
                .stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());

        List<Map<String,String>> list = Lists.newArrayList();
        collect.forEach(it->{
            long count = list.stream().filter(item -> it.getActivityId().equals(item.get("id") + "")).count();
            if(count == 0 && !task.getTaskDefinitionKey().equals(it.getActivityId())){//1.不存在2.不等于当前节点
                Map<String, String> m = Maps.newHashMap();
                //业务操作
                m.put("id", it.getActivityId());
                m.put("name", it.getActivityName());
                list.add(m);
            }
        });
        Map<String, Object> map = Maps.newHashMap();
        map.put("history_nodes", list);
        map.put("current_node", task.getTaskDefinitionKey());
        map.put("current_name", task.getName());
        return new ResultUtil<>().setData(map);
    }
}
